package com.hjy.backend.tableManager;


import com.hjy.backend.dataManager.DataManager;
import com.hjy.backend.parser.statement.*;
import com.hjy.backend.tableManager.result.BeginRes;
import com.hjy.common.error.Error;
import com.hjy.backend.utils.Parser;
import com.hjy.backend.versionManager.VersionManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TableManagerImpl implements TableManager {
    VersionManager vm;
    DataManager dm;
    private Starter starter; // 用于获取第一个表的 uid
    private Map<String, Table> tableCache; // 所有的 table
    private Map<Long, List<Table>> xidTableCache; // 事务中对应创建的表
    private Lock lock;

    TableManagerImpl(VersionManager vm, DataManager dm, Starter starter) {
        this.vm = vm;
        this.dm = dm;
        this.starter = starter;
        this.tableCache = new HashMap<>();
        this.xidTableCache = new HashMap<>();
        lock = new ReentrantLock();
        loadTables();
    }

    /**
     * 加载所有的table
     */
    private void loadTables() {
        long uid = firstTableUid();
        while (uid != 0) {
            Table tb = Table.loadTable(this, uid);
            uid = tb.nextUid;
            tableCache.put(tb.name, tb);
        }
    }

    /**
     * 加载第一个table 的 uid
     *
     * @return
     */
    private long firstTableUid() {
        byte[] raw = starter.load();
        return Parser.parseLong(raw);
    }

    /**
     * 由于是头插法, 新增一个 table 我们需要更新 .bt 文件中存储的table uid
     *
     * @param uid
     */
    private void updateFirstTableUid(long uid) {
        byte[] raw = Parser.long2Byte(uid);
        starter.update(raw);
    }

    /**
     * begin 语句, 开始一个事务
     *
     * @param begin
     * @return
     */
    @Override
    public BeginRes begin(Begin begin) {
        BeginRes res = new BeginRes();
        int level = begin.isRepeatableRead ? 1 : 0;
        // 事务, 启动
        res.xid = vm.begin(level);
        res.result = "begin".getBytes();
        return res;
    }


    /**
     * commit 语句, 提交一个事务
     *
     * @param xid
     * @return
     * @throws Exception
     */
    @Override
    public byte[] commit(long xid) throws Exception {
        vm.commit(xid);
        return "commit".getBytes();
    }

    /**
     * 撤销一个事务
     *
     * @param xid
     * @return
     */
    @Override
    public byte[] abort(long xid) {
        vm.abort(xid);
        return "abort".getBytes();
    }

    /**
     * show 语句展示现在有哪些表
     *
     * @param xid
     * @return
     */
    @Override
    public byte[] show(long xid) {
        lock.lock();
        try {
            StringBuilder sb = new StringBuilder();
            for (Table tb : tableCache.values()) {
                sb.append(tb.toString()).append("\n");
            }
            List<Table> t = xidTableCache.get(xid);
            if (t == null) {
                return "\n".getBytes();
            }
            for (Table tb : t) {
                sb.append(tb.toString()).append("\n");
            }
            return sb.toString().getBytes();
        } finally {
            lock.unlock();
        }
    }

    /**
     * create 建表语句
     *
     * @param xid
     * @param create
     * @return
     * @throws Exception
     */
    @Override
    public byte[] create(long xid, Create create) throws Exception {
        lock.lock();
        try {
            if (tableCache.containsKey(create.tableName)) {
                throw Error.DuplicatedTableException;
            }
            // 头插法
            Table table = Table.createTable(this, firstTableUid(), xid, create);
            updateFirstTableUid(table.uid);
            // 加入 table 缓冲中
            tableCache.put(create.tableName, table);
            if (!xidTableCache.containsKey(xid)) {
                xidTableCache.put(xid, new ArrayList<>());
            }
            // 这个事务中添加的表
            xidTableCache.get(xid).add(table);
            return ("create " + create.tableName).getBytes();
        } finally {
            lock.unlock();
        }
    }

    /**
     * insert 语句
     *
     * @param xid
     * @param insert
     * @return
     * @throws Exception
     */
    @Override
    public byte[] insert(long xid, Insert insert) throws Exception {
        lock.lock();
        Table table = tableCache.get(insert.tableName);
        lock.unlock();
        if (table == null) {
            throw Error.TableNotFoundException;
        }
        table.insert(xid, insert);
        return "insert".getBytes();
    }

    /**
     * select 语句
     *
     * @param xid
     * @param read
     * @return
     * @throws Exception
     */
    @Override
    public byte[] read(long xid, Select read) throws Exception {
        lock.lock();
        Table table = tableCache.get(read.tableName);
        lock.unlock();
        if (table == null) {
            throw Error.TableNotFoundException;
        }
        return table.read(xid, read).getBytes();
    }

    /**
     * update 语句
     *
     * @param xid
     * @param update
     * @return
     * @throws Exception
     */
    @Override
    public byte[] update(long xid, Update update) throws Exception {
        lock.lock();
        Table table = tableCache.get(update.tableName);
        lock.unlock();
        if (table == null) {
            throw Error.TableNotFoundException;
        }
        int count = table.update(xid, update);
        return ("update " + count).getBytes();
    }

    /**
     * delete 语句
     *
     * @param xid
     * @param delete
     * @return
     * @throws Exception
     */
    @Override
    public byte[] delete(long xid, Delete delete) throws Exception {
        lock.lock();
        Table table = tableCache.get(delete.tableName);
        lock.unlock();
        if (table == null) {
            throw Error.TableNotFoundException;
        }
        int count = table.delete(xid, delete);
        return ("delete " + count).getBytes();
    }
}
